#include "GenerateDOTFile.h"
#include "ParseNode.h"
#include "Symbol.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

UInt c;

static const char* parseNodeTypeStrings[] =
{
	"PNT_ASSIGN",
	"PNT_ADD_ASSIGN",
	"PNT_SUB_ASSIGN",

	"PNT_MUL_ASSIGN",
	"PNT_DIV_ASSIGN",

	"PNT_DIV",
	"PNT_MUL",
	"PNT_ADD",
	"PNT_SUB",

	"PNT_GOTO",

	"PNT_PRE_INCREMENT",
	"PNT_POST_INCREMENT",
	"PNT_PRE_DECREMENT",
	"PNT_POST_DECREMENT",

	"PNT_IDENTIFIER",
	"PNT_TEMP_VAR",
	"PNT_CONST"
};

static void PutConstParseNodeIntoBuff(char* buff, struct ConstParseNode* cpn, UInt* c)
{
	switch (cpn->value.type)
	{
	case CT_INT:
	case CT_INT8:
	case CT_INT16:
	case CT_INT32:
		sprintf(buff, "%d[%u]", cpn->value.i, (*c)++);
		break;
	case CT_INT64:
		sprintf(buff, "%ld[%u]", cpn->value.i64, (*c)++);
		break;

	case CT_UINT:
	case CT_UINT8:
	case CT_UINT16:
	case CT_UINT32:
		sprintf(buff, "%u[%u]", cpn->value.u, (*c)++);
		break;
	case CT_UINT64:
		sprintf(buff, "%lu[%u]", cpn->value.u64, (*c)++);
		break;

	case CT_FLOAT:
	case CT_FLOAT32:
	case CT_FLOAT64:
		sprintf(buff, "%f[%u]", cpn->value.f64, (*c)++);
		break;
	}
}

static void GenerateDOT_R(FILE* dotFile, struct ParseNode* pn, char* parentName)
{
	UInt finalBuffSize = 0;
	Int8 finalBuff[0x100];
	char* me;
	struct ParseNode* arg0 = PSUB_PARSENODE(pn, struct TwoArgParseNode)->arg0;
	struct ParseNode* arg1 = PSUB_PARSENODE(pn, struct TwoArgParseNode)->arg1;

	// Determine me
	switch (pn->type)
	{
	case PNT_ASSIGN:
	case PNT_ADD_ASSIGN:
	case PNT_SUB_ASSIGN:
	case PNT_MUL_ASSIGN:
	case PNT_DIV_ASSIGN:
	case PNT_DIV:
	case PNT_MUL:
	case PNT_ADD:
	case PNT_SUB:
	case PNT_GOTO:
	case PNT_PRE_INCREMENT:
	case PNT_POST_INCREMENT:
	case PNT_PRE_DECREMENT:
	case PNT_POST_DECREMENT:
	case PNT_TEMP_VAR:
		me = malloc(strlen(parseNodeTypeStrings[pn->type]) + 1);
		sprintf(me, "%s[%u]", parseNodeTypeStrings[pn->type], c++);
		break;

	case PNT_IDENTIFIER:
		me = malloc(strlen(PSUB_PARSENODE(pn, struct IdentifierParseNode)->identifier->name) + 1);
		sprintf(me, "%s[%u]", PSUB_PARSENODE(pn, struct IdentifierParseNode)->identifier->name, c++);
		break;

	case PNT_CONST:
		me = malloc(0x100);
		PutConstParseNodeIntoBuff(me, PSUB_PARSENODE(pn, struct ConstParseNode), &c);
		break;
	}

	finalBuffSize = sprintf(finalBuff, "\"%s\" -- \"%s\";\n", parentName, me);
	fwrite(finalBuff, sizeof(char), finalBuffSize, dotFile);

	switch (pn->type)
	{
	case PNT_ASSIGN:
	case PNT_ADD_ASSIGN:
	case PNT_SUB_ASSIGN:
	case PNT_MUL_ASSIGN:
	case PNT_DIV_ASSIGN:
	case PNT_DIV:
	case PNT_MUL:
	case PNT_ADD:
	case PNT_SUB:
		GenerateDOT_R(dotFile, arg0, me);
		GenerateDOT_R(dotFile, arg1, me);
		break;

	case PNT_GOTO:
	case PNT_PRE_INCREMENT:
	case PNT_POST_INCREMENT:
	case PNT_PRE_DECREMENT:
	case PNT_POST_DECREMENT:
	case PNT_IDENTIFIER:
	case PNT_TEMP_VAR:
	case PNT_CONST:
		break;
	}
}

void GenerateDOTFile(const char* filePath)
{
	static const char str[] = "graph graphname {\n";
	FILE* dotFile;
	struct ParseNode** pn;

	c = 1;

	dotFile = fopen(filePath, "w");
	fwrite(str, sizeof(char), sizeof(str) - 1, dotFile);

	for (pn = statements + 1; *pn; ++pn)
	{
		Int8 startName[0x100];

		struct ParseNode* arg0 = PSUB_PARSENODE(*pn, struct TwoArgParseNode)->arg0;
		struct ParseNode* arg1 = PSUB_PARSENODE(*pn, struct TwoArgParseNode)->arg1;

		sprintf(startName, "%s[%u]", parseNodeTypeStrings[(*pn)->type], c);

		switch ((*pn)->type)
		{
		case PNT_ASSIGN:
		case PNT_ADD_ASSIGN:
		case PNT_SUB_ASSIGN:
		case PNT_MUL_ASSIGN:
		case PNT_DIV_ASSIGN:
		case PNT_DIV:
		case PNT_MUL:
		case PNT_ADD:
		case PNT_SUB:
			GenerateDOT_R(dotFile, arg0, startName);
			GenerateDOT_R(dotFile, arg1, startName);
			break;

		case PNT_GOTO:
		case PNT_PRE_INCREMENT:
		case PNT_POST_INCREMENT:
		case PNT_PRE_DECREMENT:
		case PNT_POST_DECREMENT:
		case PNT_TEMP_VAR:
		case PNT_IDENTIFIER:
		case PNT_CONST:
			break;
		}
	}


	fwrite("}\n", sizeof(char), 2, dotFile);

	fclose(dotFile);
}
